package org.lee.hello;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.pool.TypePool;
import org.lee.hello.util.ClassUtil;
import org.lee.hello.util.StringUtil;

import java.lang.reflect.Modifier;

/**
 * @author jackielee
 * @version 1.0
 * @description NameClass
 * @date 2021/7/28
 **/
public class NameClass {
    public static void main(String[] args) {
        create1();
        create2();
        redefineCls();
        redefineCls2();
//        new ByteBuddy().subclass()// 定义子类
//        new ByteBuddy().rebase() // 类型重定基底，不替换原有的实现，只是将原有的改了个名称，还可以调用
//        new ByteBuddy().redefine() // 类型重定义，直接替换原来的实现
    }

    private static void create1() {
        DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
                .with(new NamingStrategy.AbstractBase() {
                    @Override
                    protected String name(TypeDescription superClass) {
                        return "org.lee.ByteBuddy." + superClass.getSimpleName();
                    }
                })
                .subclass(Object.class)
                .make();
//        dynamicType.saveIn() // 保存到文件
//        dynamicType.inject() // 将类注入到现有的jar文件中
        ClassUtil.outputClass(dynamicType.getBytes(), "DemoStr");
        /**
         package org.lee.ByteBuddy;

         public class Object {
         public Object() {
         }
         }
         */
    }

    private static void create2() {
        DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
                .with(new NamingStrategy.SuffixingRandom("suffix"))
                .subclass(Object.class)
                .make();
        ClassUtil.outputClass(dynamicType.getBytes(), "NameStr");
        /**
         package net.bytebuddy.renamed.java.lang;

         public class Object$suffix$ytJDh1je {
         public Object$suffix$ytJDh1je() {
         }
         }
         */
    }

    private static void redefineCls() {
        ByteBuddyAgent.install();
        Foo foo = new Foo();
        System.out.println(StringUtil.format("before: {}", foo.m()));
        new ByteBuddy()
                .redefine(Bar.class)
                .name(Foo.class.getName())
                .make()
                .load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
        System.out.println(StringUtil.format("after: {}", foo.m()));
    }

    private static void redefineCls2() {
        TypePool typePool = TypePool.Default.ofPlatformLoader();//of(NameClass.class.getClassLoader());
        new ByteBuddy()
                .redefine(typePool.describe("org.lee.hello.foo.Bar").resolve(), // do not use 'Bar.class'
                        ClassFileLocator.ForClassLoader.ofPlatformLoader())
                .defineField("name", String.class, Modifier.PRIVATE)
                .make()
                .load(ClassLoader.getSystemClassLoader());
        try {
            System.out.println(Bar.class.getDeclaredField("name"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

class Foo {
    String m() {
        return "foo";
    }
}

class Bar {
    String m() {
        return "bar";
    }
}